
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>消息异常处理方案 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>消息异常处理方案 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在RocketMQ中，同步发送和异步发送的内置重试机制有何核心区别？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">核心区别在于默认行为：同步发送默认会重试2次（总共尝试3次），而异步发送默认不进行重试。两者都可以通过特定的方法进行配置，例如 `setRetryTimesWhenSendFailed()` 用于同步发送，`setRetryTimesWhenSendAsyncFailed()` 用于异步发送。</div>
          </div>
          <div class="card-source">来源: 内置重试机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">RocketMQ生产者如何启用延迟容错机制，以智能地规避响应慢的Broker？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过调用 `producer.setSendLatencyFaultEnable(true)` 可以启用延迟容错机制。启用后，客户端会根据历史发送延迟情况，在后续发送消息时智能地规避那些延迟较高的Broker，并尝试选择其他可用的Broker。</div>
          </div>
          <div class="card-source">来源: 故障转移机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">当内置重试机制仍无法成功发送消息时，文档推荐了哪两种应用层策略来保证最终一致性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">文档推荐了两种应用层策略：1. **消息持久化到本地**：发送失败时将消息存入本地存储（如数据库或文件），通过定时任务重新发送。2. **消息事务表**：将业务操作和消息发送记录在同一个数据库事务中，通过定时任务检查并重试未成功发送的消息。</div>
          </div>
          <div class="card-source">来源: 应用层重试策略</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">当RocketMQ消费者消费消息失败后，消息会进入什么队列？其命名规则是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">消费失败的消息会进入一个专用的重试队列。重试队列的命名规则是 “%RETRY%” 加上消费组的名称，即 “%RETRY%+消费组名”。</div>
          </div>
          <div class="card-source">来源: 消费重试机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">概念</div>
          <div class="card-question">什么是死信队列（DLQ），消息在什么条件下会进入该队列？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">概念</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">死信队列（Dead-Letter Queue）是用于存放无法被成功消费的消息的特殊队列。当一条消息的重试次数超过了设定的最大重试次数后（默认为16次，可配置），RocketMQ会认为这条消息无法处理，并将其投递到死信队列中，等待人工干预。</div>
          </div>
          <div class="card-source">来源: 死信队列处理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">为什么在RocketMQ的消费端需要保证幂等性？文档中列举了哪些实现方法？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为RocketMQ的消费重试机制会导致同一条消息可能被消费者多次接收和处理，所以必须保证消费操作的幂等性，以避免重复消费导致业务数据错误。文档中列举的实现方法有：使用消息ID或业务ID作为幂等键、在消费前检查消息是否已处理、使用数据库唯一索引或分布式锁。</div>
          </div>
          <div class="card-source">来源: 消费幂等性保证</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">如何通过异步发送方式来确认RocketMQ消息的发送结果？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">异步发送通过实现`SendCallback`接口来获取发送结果。在调用`producer.send`方法时传入该接口的实例，如果发送成功，会回调`onSuccess`方法；如果发生异常，则会回调`onException`方法。开发者可以在这两个回调方法中分别处理成功和失败的逻辑。</div>
          </div>
          <div class="card-source">来源: 发送结果确认</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">当消费失败时，RocketMQ的重试间隔是如何设计的？默认的最大重试次数是多少？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">RocketMQ的重试间隔采用指数退避算法。例如，第一次重试间隔为10秒，第二次为30秒，以此类推，间隔时间会逐渐增长。默认的最大重试次数是16次，最长的重试间隔为2小时。</div>
          </div>
          <div class="card-source">来源: 消费重试机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">文档中提到了哪三种控制消费速率的限流方法？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">文档中提到了三种消费限流的配置方法：1. 通过 `consumer.setPullThresholdForQueue()` 设置每个队列在本地缓存的最大消息数。2. 通过 `consumer.setPullBatchSize()` 设置消费者单次从Broker拉取的消息数量。3. 通过 `consumer.setConsumeThreadMin()` 和 `consumer.setConsumeThreadMax()` 来控制消费线程池的大小。</div>
          </div>
          <div class="card-source">来源: 消费限流与熔断</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
